#ifndef __VECT__
#define __VECT__
#include <cmath>

class vect {
public:
	double x,y,z;

	inline vect(const double fx, const double fy, const double fz = 0) : x(fx), y(fy), z(fz) {};
	inline vect(const double f = 0) : x(f), y(f), z(f) {};
	inline vect(const vect& f) : x(f.x), y(f.y), z(f.z) {};
	inline vect& operator = (const vect& f) { x = f.x; y = f.y; z = f.z; return *this; };

	inline operator double* () { return &x; };
	inline operator const double* () const { return &x; };
	
	inline vect operator - () const { return vect(-x, -y, -z); };
	
	inline bool operator == (const vect& f) const { return (x == f.x && y == f.y && z == f.z); };
	inline bool operator != (const vect& f) const { return (x != f.x || y != f.y || z != f.z); };

	inline vect operator + (const vect& f) const { return vect(x + f.x, y + f.y, z + f.z); };
	inline vect& operator += (const vect& f) { x += f.x; y += f.y; z += f.z; return *this; };
	inline vect operator - (const vect& f) const { return vect(x - f.x, y - f.y, z - f.z); };
	inline vect& operator -= (const vect& f) { x -= f.x; y -= f.y; z -= f.z; return *this; };
	inline vect operator * (const vect& f) const { return vect(x * f.x, y * f.y, z * f.z); };
	inline vect operator * (const double f) const { return vect(x * f, y * f, z * f); };
	inline vect& operator *= (const vect& f) { x *= f.x; y *= f.y; z *= f.z; return *this; };
	inline vect& operator *= (const double f) { x *= f; y *= f; z *= f; return *this; };
	inline vect operator / (const double f) const { return vect(x / f, y / f, z / f); };
	inline vect& operator /= (const double f) { x /= f; y /= f; z /= f; return *this; };
	
	static const vect ZERO;
	static const vect ONE;
	static const vect UP;
	static const vect DOWN;
	static const vect FORWARD;
	static const vect BACKWARD;
	static const vect LEFT;
	static const vect RIGHT;
	
	inline double length() const { return sqrt(x * x + y * y + z * z); }
	inline double lengthSquared() const { return x * x + y * y + z * z; };
	inline double distance(const vect& f) const { return (*this - f).length(); };
	inline double distanceSquared(const vect& f) const { return (*this - f).lengthSquared(); };
	
	inline vect normalized() const { return *this / this->length(); };
	inline vect& normalize() { return *this /= this->length(); };
	
	inline double dot(const vect& f) const { return x * f.x + y * f.y + z * f.z; };
	inline vect cross(const vect& f) const { return vect(y * f.z - z * f.y, z * f.x - x * f.z, x * f.y - y * f.x); };
	
	inline double angleBetween(const vect& f) const { return acos(this->dot(f) / sqrt(this->lengthSquared() * f.lengthSquared())); };
	inline vect reflect(const vect& f) const { return *this - f * 2 * this->dot(f) / f.lengthSquared(); };
	
	inline vect clamp(const vect& vmin, const vect& vmax) const {
		return vect(
			 (x < vmin.x)?vmin.x:(x > vmax.x)?vmax.x:x,
			 (y < vmin.y)?vmin.y:(y > vmax.y)?vmax.y:y,
			 (z < vmin.z)?vmin.z:(z > vmax.z)?vmax.z:z);
	}

	inline vect lerp(const vect& f, double r) { return *this * (1 - r) + f * r; }
};

#endif
